/*!
\copyright  Copyright (c) 2023 Qualcomm Technologies International, Ltd.\n
            All Rights Reserved.\n
            Qualcomm Technologies International, Ltd. Confidential and Proprietary.
\file
\ingroup    sensor_subsystem
\brief      Sensor subsystem support
*/

#include "sensor_subsystem_private.h"

#ifndef TRAPSET_SENSORSS
#error "TRAPSET_SENSORSS not defined"
#endif

#include <sensor_ss.h>
#include <logging.h>
#include <panic.h>

#ifdef INCLUDE_ERPC
#include "erpc_transport_setup.h"
#include "erpc_mbf_setup.h"
#include "erpc_client_setup.h"
#endif

sensor_subsystem_data_t sensor_subsystem = {0};

static void sensorSubsystem_MessageHandler(Task task, MessageId id, Message message);

static TaskData sensor_subsystem_task = {.handler = sensorSubsystem_MessageHandler};
#define sensorSubsystem_getTask()   &sensor_subsystem_task

/* The ERPC code being added for sensor subsystem messaging uses assert and 
   is not re-directed to a panic. Add __assert_func to do that redirection  */
void __assert_func (const char *file, int line,  const char *function, const char *assert)
{
    UNUSED(file);
    UNUSED(line);
    UNUSED(function);
    UNUSED(assert);

    Panic();
}

void sensorSubsystem_resetInternalState(void)
{
    sensorSubsystem_setState(SENSOR_SUBSYS_STATE_OFF);
    sensor_subsystem.file_id = FILE_NONE;
    sensor_subsystem.sensor_startup_flags = 0;
}

void sensorSubsystem_setState(sensor_subsystem_state_t state)
{
    if (sensor_subsystem.state == state)
    {
        /* Not an error but transitions should be simple */
        DEBUG_LOG_INFO("sensorSubsystem_setState Requested transition to same state enum:sensor_subsystem_state_t:%d", state);
    }
    else
    {
        DEBUG_LOG_STATE("sensorSubsystem_setState state changed from enum:sensor_subsystem_state_t:%d to enum:sensor_subsystem_state_t:%d",
                    sensor_subsystem.state, state);
    }
    sensor_subsystem.state = state;
}

sensor_subsystem_state_t sensorSubsystem_getState(void)
{
    return sensor_subsystem.state;
}

bool SensorSubsystem_Init(Task init_task)
{
    UNUSED(init_task);

    sensorSubsystem_resetInternalState();
    sensor_subsystem.clients = PanicNull(TaskList_Create());

    return TRUE;
}

void SensorSubsystem_LoadImage(Task init_task, FILE_INDEX file_id, uint32 startup_flags)
{
    sensor_subsystem.init_task = init_task;
    if (sensorSubsystem_getState() != SENSOR_SUBSYS_STATE_OFF)
    {
        Panic();
    }

    sensor_subsystem.sensor_startup_flags = startup_flags;
    sensor_subsystem.file_id = file_id;

    MessageSend(sensorSubsystem_getTask(), SENSOR_SUBSYSTEM_INTERNAL_START_SS, NULL);
}

void SensorSubsystem_Register(Task client_task)
{
    PanicFalse(TaskList_AddTask(sensor_subsystem.clients, client_task));
    if (sensorSubsystem_getState() == SENSOR_SUBSYS_STATE_STARTED)
    {
        MessageSend(client_task, SENSOR_SUBSYSTEM_STARTED_IND, NULL);
    }
}

void SensorSubsystem_Unregister(Task client_task)
{
    TaskList_RemoveTask(sensor_subsystem.clients, client_task);

    if (TaskList_Size(sensor_subsystem.clients) == 0)
    {
        SensorSsPowerOff();
        sensorSubsystem_resetInternalState();

        MessageSend(client_task, SENSOR_SUBSYSTEM_STOPPED_IND, NULL);
    }
    else
    {
        MessageSend(client_task, SENSOR_SUBSYSTEM_UNREGISTER_CFM, NULL);
    }
}

/*! Generic initialisation for ERPC transport.

    This does not require any knowledge of the function being ERPC'd
    so can always be called (if ERPC supported)
 */
static void sensorSubsystem_InitialiseErpcTransport(void)
{
#ifdef INCLUDE_ERPC
    erpc_transport_t transport;
    transport = PanicNull(erpc_transport_traps_init());

    erpc_mbf_t message_buffer_factory;
    message_buffer_factory = PanicNull(erpc_mbf_static_init());

    erpc_client_init(transport, message_buffer_factory);
#endif
}

static void sensorSubsystem_startSensorSs(void)
{
#ifdef MESSAGE_SENSOR_SS_COMMS_STATUS
    MessageSensorSsTask(sensorSubsystem_getTask());
#endif

    PanicFalse(SensorSsLoad(sensor_subsystem.file_id, sensor_subsystem.sensor_startup_flags, NULL));

    sensorSubsystem_InitialiseErpcTransport();

#ifndef MESSAGE_SENSOR_SS_COMMS_STATUS
    MessageSendLater(sensorSubsystem_getTask(),
                     SENSOR_SUBSYSTEM_INTERNAL_COMMS_DELAY_TIMER, NULL,
                     sensorSubsystemConnectedDelayMs());
#endif
}

#ifdef MESSAGE_SENSOR_SS_COMMS_STATUS
static void sensor_subsystem_handle_comms_status(const MessageSensorSsCommsStatus *comms_status)
{
    if (   comms_status->transport_active 
        && sensorSubsystem_getState() == SENSOR_SUBSYS_STATE_STARTING)
    {
        sensorSubsystem_setState(SENSOR_SUBSYS_STATE_STARTED);
        MessageSend(sensor_subsystem.init_task, SENSOR_SUBSYSTEM_STARTED_IND, NULL);
        if (TaskList_Size(sensor_subsystem.clients))
        {
            TaskList_MessageSendId(sensor_subsystem.clients, SENSOR_SUBSYSTEM_STARTED_IND);
        }
    }
}
#endif

static void sensorSubsystem_MessageHandler(Task task, MessageId id, Message message)
{
    UNUSED(task);
    UNUSED(message);

    DEBUG_LOG("sensorSubsystem_MessageHandler MSG:sensor_subsystem_internal_msg_t:0x%x", id);

    switch (id)
    {
        case SENSOR_SUBSYSTEM_INTERNAL_START_SS:
            sensorSubsystem_setState(SENSOR_SUBSYS_STATE_STARTING);
            sensorSubsystem_startSensorSs();
            break;

#ifdef MESSAGE_SENSOR_SS_COMMS_STATUS
        case MESSAGE_SENSOR_SS_COMMS_STATUS:
            sensor_subsystem_handle_comms_status((const MessageSensorSsCommsStatus *)message);
            break;
#else

        case SENSOR_SUBSYSTEM_INTERNAL_COMMS_DELAY_TIMER:
            sensor_subsystem.ready_timer_expired = TRUE;
            sensorSubsystem_setState(SENSOR_SUBSYS_STATE_STARTED);
            MessageSend(sensor_subsystem.init_task, SENSOR_SUBSYSTEM_STARTED_IND, NULL);
            if (TaskList_Size(sensor_subsystem.clients))
            {
                TaskList_MessageSendId(sensor_subsystem.clients, SENSOR_SUBSYSTEM_STARTED_IND);
            }
            break;
#endif

        default:
            break;
    }
}

